/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.io;

import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGFieldList;
import org.jfab.core.collection.CGMethodDeclarationList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.factory.TypeReferenceSupport;
import org.jfab.core.formatter.Formatter;
import org.jfab.core.model.CGField;
import org.jfab.core.model.CGInterface;
import org.jfab.core.model.CGMethodDeclaration;

/**
 * Provides an interface writer.
 */
public final class CGInterfaceWriter extends
        CGCompilationUnitWriter<CGInterface>
{
    /** Method writer. */
    private final CGWriter<CGMethodDeclaration> _methodWriter;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeReferenceSupport Type reference support.
     * @param stringFactory String factory.
     * @param basicCodeGenerator Basic code generator.
     * @param fieldWriter Field writer.
     * @param formatter Code formatter.
     * 
     * @param methodWriter Method writer.
     */
    public CGInterfaceWriter(final TypeReferenceSupport typeReferenceSupport,
            final StringFactory stringFactory,
            final BasicCodeGenerator basicCodeGenerator,
            final CGWriter<CGField> fieldWriter, final Formatter formatter,
            final CGWriter<CGMethodDeclaration> methodWriter)
    {
        super(typeReferenceSupport, stringFactory, basicCodeGenerator,
                fieldWriter, formatter);

        _methodWriter = methodWriter;
    }

    /**
     * Write an interface.
     * 
     * @param writer Writer.
     * @param cgInterface Interface.
     */
    @Override
    public final void write(final Writer writer, final CGInterface cgInterface)
    {
        Validate.notNull(cgInterface, "cgInterface is null");

        final TypeReferenceSupport typeReferenceSupport = getTypeReferenceSupport();
        typeReferenceSupport.clear();
        typeReferenceSupport.determineReferencedTypes(cgInterface);

        final String packageName = cgInterface.getPackageName();
        final CommentList commentLines = new CommentList(
                cgInterface.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                cgInterface.getAnnotations());
        final ModifierList modifiers = new ModifierList(
                cgInterface.getModifiers());
        final String interfaceName = cgInterface.getName();
        final List<String> typeParameters = cgInterface.getTypeParameters();
        final CGTypeList extendsTypes = new CGTypeList(
                cgInterface.getExtendsTypes());
        final Set<String> importClasses = typeReferenceSupport
                .getImportTypeNames(cgInterface);

        final StringWriter stringWriter = new StringWriter();

        writePackageDeclaration(stringWriter, packageName);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        writeImports(stringWriter, importClasses);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        getBasicCodeGenerator().writeMultilineJavadocComment(stringWriter,
                commentLines);
        writeInterfaceDeclaration(stringWriter, annotations, modifiers,
                interfaceName, typeParameters, extendsTypes);
        getBasicCodeGenerator().writeOpenBrace(stringWriter);

        writeFields(stringWriter, new CGFieldList(cgInterface.getFields()));
        writeMethods(stringWriter,
                new CGMethodDeclarationList(cgInterface.getMethods()));

        getBasicCodeGenerator().writeCloseBrace(stringWriter);

        format(writer, stringWriter);
    }

    /**
     * @return the methodWriter
     */
    protected CGWriter<CGMethodDeclaration> getMethodWriter()
    {
        return _methodWriter;
    }

    /**
     * Write methods.
     * 
     * @param writer Writer.
     * @param methods Methods.
     */
    protected void writeMethods(final Writer writer,
            final CGMethodDeclarationList methods)
    {
        if (CollectionUtils.isNotEmpty(methods))
        {
            final BasicCodeGenerator basicCodeGenerator = getBasicCodeGenerator();
            final CGMethodDeclarationWriter methodDeclarationWriter = (CGMethodDeclarationWriter)getMethodWriter();

            for (final CGMethodDeclaration cgMethod : methods)
            {
                basicCodeGenerator.writeBlankLine(writer);
                methodDeclarationWriter.write(writer, cgMethod);
            }
        }
    }

    /**
     * @param writer Writer.
     * @param annotations Annotations.
     * @param modifiers Modifiers (@see java.lang.reflect.Modifier)
     * @param interfaceName Interface name.
     * @param typeParameters Type parameters.
     * @param extendsTypes Super classes.
     */
    private void writeInterfaceDeclaration(final Writer writer,
            final CGAnnotationList annotations, final ModifierList modifiers,
            final String interfaceName, final List<String> typeParameters,
            final CGTypeList extendsTypes)
    {
        Validate.notNull(writer, "writer is null");
        Validate.notEmpty(interfaceName, "interfaceName is empty");

        final String modifiersString = getStringFactory()
                .createModifiersString(modifiers);
        final String typeParametersString = getStringFactory()
                .createTypeParametersString(typeParameters);
        final String extendsString = getStringFactory().createExtendsString(
                extendsTypes);

        getBasicCodeGenerator().writeLine(
                writer,
                modifiersString + "interface " + interfaceName
                        + typeParametersString + extendsString);
    }
}
